using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace BlogEngine.Core
{
	/// <summary>
	/// A generic collection with the ability to 
	/// check if it has been changed.
	/// </summary>
	[Serializable]
	public class StateCollection<T> : Collection<T>, ISupportInitialize
	{
		/// <summary>
		/// Finds all.
		/// </summary>
		/// <param name="match">The match.</param>
		/// <returns></returns>
		public List<T> FindAll(Predicate<T> match)
		{
			if (match == null)
				throw new ArgumentNullException("match");

			List<T> list = new List<T>();
			for (int i = 0; i < this.Count; i++)
				if (match(this[i]))
					list.Add(this[i]);

			return list;
		}

		/// <summary>
		/// Adds the range.
		/// </summary>
		/// <param name="collection">The collection.</param>
		public void AddRange(IEnumerable<T> collection)
		{
			if (collection != null)
			{
				IEnumerator<T> list = collection.GetEnumerator();

				while (list.MoveNext())
					Add(list.Current);
			}
		}

		#region Base overrides

		/// <summary>
		/// Inserts an element into the collection at the specified index and marks it changed.
		/// </summary>
		/// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
		/// <param name="item">The object to insert. The value can be null for reference types.</param>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		/// 	<paramref name="index"/> is less than zero.-or-<paramref name="index"/> is greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.</exception>
		protected override void InsertItem(int index, T item)
		{
			base.InsertItem(index, item);

			if (!Initializing)
				_IsChanged = true;
		}

		/// <summary>
		/// Removes all the items in the collection and marks it changed.
		/// </summary>
		protected override void ClearItems()
		{
			base.ClearItems();

			if (!Initializing)
				_IsChanged = true;
		}

		/// <summary>
		/// Removes the element at the specified index and marks the collection changed.
		/// </summary>
		/// <param name="index">The zero-based index of the element to remove.</param>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		/// 	<paramref name="index"/> is less than zero.-or-<paramref name="index"/> is equal to or greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.</exception>
		protected override void RemoveItem(int index)
		{
			base.RemoveItem(index);

			if (!Initializing)
				_IsChanged = true;
		}

		/// <summary>
		/// Replaces the element at the specified index and marks the collection changed.
		/// </summary>
		/// <param name="index">The zero-based index of the element to replace.</param>
		/// <param name="item">The new value for the element at the specified index. The value can be null for reference types.</param>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		/// 	<paramref name="index"/> is less than zero.-or-<paramref name="index"/> is greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.</exception>
		protected override void SetItem(int index, T item)
		{
			base.SetItem(index, item);

			if (!Initializing)
				_IsChanged = true;
		}

		#endregion

		private bool _IsChanged;
		/// <summary>
		/// Gets if this object's data has been changed.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is changed; otherwise, <c>false</c>.
		/// </value>
		/// <returns>A value indicating if this object's data has been changed.</returns>
		public virtual bool IsChanged
		{
			get { return _IsChanged; }
			set { _IsChanged = value; }
		}

		/// <summary>
		/// To the array.
		/// </summary>
		/// <returns></returns>
		public T[] ToArray()
		{
			T[] destinationArray = new T[this.Count];
			this.CopyTo(destinationArray, 0);
			return destinationArray;
		}

		#region ISupportInitialize Members

		/// <summary>
		/// Gets or sets a value indicating whether this <see cref="BusinessBase&lt;TYPE, KEY&gt;"/> is initializing.
		/// </summary>
		/// <value><c>true</c> if initializing; otherwise, <c>false</c>.</value>
		protected bool Initializing { get; set; }

		/// <summary>
		/// Signals the object that initialization is starting.
		/// </summary>
		public void BeginInit()
		{
			Initializing = true;
		}

		/// <summary>
		/// Signals the object that initialization is complete.
		/// </summary>
		public void EndInit()
		{
			Initializing = false;
		}

		#endregion
	}
}